package at.fhj.pse.domain.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import at.fhj.pse.datasource.model.TimeInterval;
import at.fhj.pse.domain.exception.DomainLayerException;

public class DayIntervalData implements Comparable<DayIntervalData> {

	private List<TimeInterval> allIntervals = new ArrayList<TimeInterval>();
	private List<TimeInterval> intervalsWithFactorOnePointFive = new ArrayList<TimeInterval>();
	private List<TimeInterval> intervalsWithFactorOne = new ArrayList<TimeInterval>();
	private long breakSumInMinutes = 0;
	private long actualTimeInMinutes = 0;
	private long allocatedTimeInMinutes = 0;
	private long timeOfIntervalsWithFactorOne = 0;
	private long timeOfIntervalsWithFactorOnePointFive = 0;

	public DayIntervalData() {}
	
	public DayIntervalData(List<TimeInterval> intervals) {
		if (intervals == null) {
			throw new DomainLayerException("intervals is null", new IllegalArgumentException());
		}
		this.allIntervals = intervals;
		Collections.sort(this.allIntervals);
		calculateData();
	}

	protected void calculateData() {
		calculateIntervalTypes();
		calculateAllocatedTime();
		calculateDayData();
	}

	protected void calculateAllocatedTime() {
		if (allIntervals.size() > 0) {
			float employmentFactor = allIntervals.get(0).getEmployee()
					.getEmploymentType();
			this.allocatedTimeInMinutes = (long) ((8 * 60) * employmentFactor);
		}
	}

	/*
	 * Berechnung der Mittagspause aufgrund folgender Fälle, welche im aktuellen
	 * Zeiterfassungs-Excelsheet durchgeprüft wurden:
	 * 
	 * 1. 08:30 - 10:30 -> kein problem = 2 h 2. 08:30 - 14:30 -> kein problem =
	 * 6 h 3. 08:30 - 14:31 = 6h (nichts wird abgezogen, es wird einfach nicht
	 * mitgezählt) 4. 08:30 - 14:59 = 6h des gleiche wie 3. 5. 08:30 - 15:00 =
	 * 6h des gleiche wie 4. und 3. 6. 08:30 - 15:01 = 6h 1m 7. 08:30 - 15:30
	 * und Unterbrechung von 14:30 - 15:30 = 6h 8. 08:30 - 16:30 und
	 * Unterbrechung von 14:00 - 14:30, also innerhalb der 6 stunden = 7:30 9.
	 * 08:30 - 16:30 und Unterbrechung von 16:00 - 16:30, also Intervalllänge
	 * von über 6 Std. = 7:30 Berechnung daher (im Falle einer Arbeit von über 6
	 * Std pro Tag): ist zwischen den Intervallen eine Pause von insgesamt 30
	 * Minuten -> keine weiteren Zeitabzüge Pause von 20 Minuten -> weitere 10
	 * Minuten abzug
	 * 
	 * 
	 * Sonderfälle: Feiertag: Aktivität=Feiertag, ist-Stunden = 8,
	 * Soll-Stunden=8 Urlaub, Krankenstand: Aktivität=Urlaub oder Krankenstand,
	 * Zeitberechnung siehe Feiertag Zeitausgleich: Aktivität=Zeitausgleich,
	 * Soll-Std=8, Ist-Stunden für das angegebene Intervall=0
	 */
	protected void calculateDayData() {
		long breakSum = 0;
		long timeSum = 0;
		Date lastTo_Date = null;

		for (TimeInterval ti : allIntervals) {
			String activityDescription = ti.getActivity().getDescription();
			if (activityDescription.equals("Feiertag")
					|| activityDescription.equals("Urlaub")
					|| activityDescription.equals("Krankenstand")) {
				this.breakSumInMinutes = 0;
				this.actualTimeInMinutes = this.allocatedTimeInMinutes;
				return;
			}
			if (lastTo_Date != null) {
				breakSum += (ti.getFrom().getTime() - lastTo_Date.getTime())
						/ (1000 * 60);
			}
			lastTo_Date = ti.getTo();

			if (!activityDescription.equals("Zeitausgleich")) {
				timeSum += (ti.getTo().getTime() - ti.getFrom().getTime())
						/ (1000 * 60);
			} else {
				breakSum += (ti.getTo().getTime() - ti.getFrom().getTime())
						/ (1000 * 60);
			}

		}

		// Calculation of the Break
		if (timeSum > (6 * 60)) {
			if (breakSum < 30) {
				long missingTime = (30 - breakSum);
				this.breakSumInMinutes = missingTime;
				timeSum = timeSum - missingTime;
			}
		} else {
			this.breakSumInMinutes = 0;
		}
		this.actualTimeInMinutes = timeSum;
	}

	protected void calculateIntervalTypes() {
		Calendar fromCal = Calendar.getInstance();
		Calendar toCal = Calendar.getInstance();

		for (TimeInterval currentInterval : allIntervals) {
			String activityDescription = currentInterval.getActivity()
					.getDescription();
			if (activityDescription.equals("Feiertag")
					|| activityDescription.equals("Urlaub")
					|| activityDescription.equals("Krankenstand")) {
				this.intervalsWithFactorOne.add(currentInterval);
				timeOfIntervalsWithFactorOne += (currentInterval.getTo()
						.getTime() - currentInterval.getFrom().getTime())
						/ (1000 * 60);
				return;
			} else if (activityDescription.equals("Zeitausgleich")) {
				this.intervalsWithFactorOne.add(currentInterval);
			} else {
				fromCal.setTime(currentInterval.getFrom());
				toCal.setTime(currentInterval.getTo());

				if (fromCal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
					this.intervalsWithFactorOnePointFive.add(currentInterval);
					timeOfIntervalsWithFactorOnePointFive += (currentInterval
							.getTo().getTime() - currentInterval.getFrom()
							.getTime())
							/ (1000 * 60);
				} else if (fromCal.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) {
					if (toCal.get(Calendar.HOUR_OF_DAY) < 20) {
						this.intervalsWithFactorOne.add(currentInterval);
						timeOfIntervalsWithFactorOne += (currentInterval
								.getTo().getTime() - currentInterval.getFrom()
								.getTime())
								/ (1000 * 60);
					} else if (toCal.get(Calendar.HOUR_OF_DAY) == 20
							&& toCal.get(Calendar.MINUTE) == 0) {
						this.intervalsWithFactorOne.add(currentInterval);
						timeOfIntervalsWithFactorOne += (currentInterval
								.getTo().getTime() - currentInterval.getFrom()
								.getTime())
								/ (1000 * 60);
					} else if (fromCal.get(Calendar.HOUR_OF_DAY) >= 20) {
						this.intervalsWithFactorOnePointFive
								.add(currentInterval);
						timeOfIntervalsWithFactorOnePointFive += (currentInterval
								.getTo().getTime() - currentInterval.getFrom()
								.getTime())
								/ (1000 * 60);
					} else {
						Calendar c = Calendar.getInstance();
						c.setTime(currentInterval.getTo());
						c.set(Calendar.HOUR_OF_DAY, 20);
						c.set(Calendar.MINUTE, 0);
						Date boundaryDate = new Date(c.getTimeInMillis());

						TimeInterval tiFactorOne = new TimeInterval();
						tiFactorOne.setFrom(currentInterval.getFrom());
						tiFactorOne.setTo(boundaryDate);
						tiFactorOne.setActivity(currentInterval.getActivity());
						tiFactorOne.setEmployee(currentInterval.getEmployee());
						intervalsWithFactorOne.add(tiFactorOne);
						timeOfIntervalsWithFactorOne += (tiFactorOne.getTo()
								.getTime() - tiFactorOne.getFrom().getTime())
								/ (1000 * 60);

						TimeInterval tiFactorOnePointFive = new TimeInterval();
						tiFactorOnePointFive.setFrom(boundaryDate);
						tiFactorOnePointFive.setTo(currentInterval.getTo());
						tiFactorOnePointFive.setActivity(currentInterval
								.getActivity());
						tiFactorOnePointFive.setEmployee(currentInterval
								.getEmployee());
						intervalsWithFactorOnePointFive
								.add(tiFactorOnePointFive);
						timeOfIntervalsWithFactorOnePointFive += (tiFactorOnePointFive
								.getTo().getTime() - tiFactorOnePointFive
								.getFrom().getTime())
								/ (1000 * 60);
					}
				} else {
					intervalsWithFactorOne.add(currentInterval);
					timeOfIntervalsWithFactorOne += (currentInterval.getTo()
							.getTime() - currentInterval.getFrom().getTime())
							/ (1000 * 60);
				}
			}
		}
	}

	// Sollzeit
	public long getAllocatedTimeInMinutes() {
		return this.allocatedTimeInMinutes;
	}

	public String getAllocatedTimeAsString() {
		int hours = (int) (this.allocatedTimeInMinutes / 60);
		int minutes = (int) (this.allocatedTimeInMinutes - (hours * 60));
		return hours + "h " + minutes + "min";
	}

	// Ist-Zeit
	public long getActualTimeInMinutes() {
		return actualTimeInMinutes;
	}

	public String getActualTimeAsString() {
		int hours = (int) (getActualTimeInMinutes() / 60);
		int minutes = (int) (getActualTimeInMinutes() - (hours * 60));
		return hours + "h " + minutes + "min";
	}

	// Differenz
	public long getDifferenceInMinutes() {
		return getActualTimeInMinutes() - getAllocatedTimeInMinutes();
	}

	public String getDifferenceAsString() {
		String sign = "";
		long differencePositive = Math.abs(getDifferenceInMinutes());
		int hours = (int) (differencePositive / 60);
		int minutes = (int) (differencePositive - (hours * 60));

		if (getDifferenceInMinutes() > 0) {
			sign = "+";
		} else {
			sign = "-";
		}
		return sign + " " + hours + "h " + minutes + "min";
	}

	public Date getDateOfIntervals() {
		if (!this.allIntervals.isEmpty()) {
			return this.allIntervals.get(0).getFrom();
		} else
			return new Date();
	}

	public long getTimeOfIntervalsWithFactorOneInMinutes() {
		return timeOfIntervalsWithFactorOne;
	}

	public String getTimeOfIntervalsWithFactorOneAsString() {
		int hours = (int) (this.timeOfIntervalsWithFactorOne / 60);
		int minutes = (int) (this.timeOfIntervalsWithFactorOne - (hours * 60));
		return hours + "h " + minutes + "min";
	}

	public long getTimeOfIntervalsWithFactorOnePointFiveInMinutes() {
		return timeOfIntervalsWithFactorOnePointFive;
	}

	public long getTimeOfIntervalsWithFactorOnePointFiveInMinutesCalculated() {
		return (long) (timeOfIntervalsWithFactorOnePointFive * 1.5);
	}

	public String getTimeOfIntervalsWithFactorOnePointFiveAsString() {
		int hours = (int) (this.timeOfIntervalsWithFactorOnePointFive / 60);
		int minutes = (int) (this.timeOfIntervalsWithFactorOnePointFive - (hours * 60));
		return hours + "h " + minutes + "min";
	}

	public String getTimeOfIntervalsWithFactorOnePointFiveCalculatedAsString() {
		int hours = (int) (getTimeOfIntervalsWithFactorOnePointFiveInMinutesCalculated() / 60);
		int minutes = (int) (getTimeOfIntervalsWithFactorOnePointFiveInMinutesCalculated() - (hours * 60));
		return hours + "h " + minutes + "min";
	}

	public String getSumOfIntervalTimes() {
		long sumMinutes = getTimeOfIntervalsWithFactorOneInMinutes()
				+ getTimeOfIntervalsWithFactorOnePointFiveInMinutesCalculated();
		int hours = (int) (sumMinutes / 60);
		int minutes = (int) (sumMinutes - (hours * 60));
		return hours + "h " + minutes + "min";
	}

	public void addInterval(TimeInterval interval) {
		if (interval == null) {
			throw new DomainLayerException("interval is null", new IllegalArgumentException());
		}
		this.allIntervals.add(interval);
		Collections.sort(this.allIntervals);
	}

	public List<TimeInterval> getIntervalsWithFactorOne() {
		return intervalsWithFactorOne;
	}

	public List<TimeInterval> getIntervalsWithFactorOnePointFive() {
		return intervalsWithFactorOnePointFive;
	}

	public long getBreakSumInMinutes() {
		return breakSumInMinutes;
	}

	public String getBreakSumAsString() {
		int hours = (int) (this.breakSumInMinutes / 60);
		int minutes = (int) (this.breakSumInMinutes - (hours * 60));
		return hours + "h " + minutes + "min";
	}

	public long getTimeSumInMinutes() {
		return getTimeOfIntervalsWithFactorOneInMinutes()
				+ getTimeOfIntervalsWithFactorOnePointFiveInMinutesCalculated();
	}

	@Override
	public int compareTo(DayIntervalData o) {
		if (o == null) {
			throw new DomainLayerException("(o) DayIntervalData is null", new IllegalArgumentException());
		}
		return (int) (this.getDateOfIntervals().getTime() - o
				.getDateOfIntervals().getTime());
	}

}